using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Me.Prettyprint.Cassandra.Connection.Client;
using Me.Prettyprint.Cassandra.Connection.Factory;
using Me.Prettyprint.Cassandra.Service;
using Me.Prettyprint.Cassandra.Utils;
using Me.Prettyprint.Hector.Api.Exceptions;

namespace Me.Prettyprint.Cassandra.Connection
{
    /// <summary>
    /// Latency aware pool will score the host and calculate the latency.
    /// This is then used to figure out which host has lower latency.
    /// </summary>
	public class LatencyAwareHClientPool : ConcurrentHClientPool
	{
		private static readonly AtomicInt intervalupdates = new AtomicInt(0);
		private const int UPDATES_PER_INTERVAL = 1000;
		private const int WINDOW_QUEUE_SIZE = 100;
		private const double SENTINEL_COMPARE = 0.768;
		private ConcurrentQueue<double> latencies;
		
		public LatencyAwareHClientPool(HClientFactory clientFactory, CassandraHost host, CassandraClientMonitor monitor):base(clientFactory, host, monitor)
		{
            // Note: we use a custom queue, since the default ConcurrentQueue doesn't support fixed size
            latencies = new FixedSizedConcurrentQueue<double>(WINDOW_QUEUE_SIZE);
		}
		public override HClient BorrowClient()
		{
			HClient client = base.BorrowClient();
			client.StartToUse();
			return client;
		}
		public override void ReleaseClient(HClient client)
		{
			Add((double)client.SinceLastUsed);
			base.ReleaseClient(client);
		}
		internal void Add(double i)
		{
            if (intervalupdates.Value >= UPDATES_PER_INTERVAL)
            {
                return;
            }
            else
            {
                latencies.Enqueue(i);
                intervalupdates.Increment();
            }
		}
		internal double Score()
		{
			double log = 0d;
			if (latencies.Count > 0)
			{
				double probability = P(SENTINEL_COMPARE);
				log = (- 1) * Math.Log10(probability);
			}
			return log;
		}
		internal double P(double t)
		{
			double mean = Mean();
			double exponent = (- 1) * (t) / mean;
			return 1 - System.Math.Pow(System.Math.E, exponent);
		}
		private double Mean()
		{
			double total = 0;
			foreach(double d in latencies)
			{
				total += d;
			}
			return total / latencies.Count;
		}
		public void ResetIntervel()
		{
            intervalupdates.Value = 0;
		}
		public void Clear()
		{
            ConcurrentQueueExtensions.Clear(latencies);
            intervalupdates.Value = 0;
		}
		public override bool Equals(System.Object obj)
		{
			return ((LatencyAwareHClientPool) obj).CassandraHost.Equals(CassandraHost);
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
	}
}